﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ERF;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using ERF.Physics;
using FirstPerson.Objects.Pickups;

namespace FirstPerson.Objects
{
    public class PlayerKillEventArgs : EventArgs {
        private Player killer, victim;
        public PlayerKillEventArgs(Player killer, Player victim) {
            this.killer = killer;
            this.victim = victim;
        }
        public Player Killer {
            get { return this.killer; }
        }
        public Player Victim {
            get { return this.victim; }
        }
    }
    public class PlayerDieEventArgs : EventArgs
    {
        private Player killer, victim;
        public PlayerDieEventArgs(Player killer, Player victim)
        {
            this.killer = killer;
            this.victim = victim;
        }
        public Player Killer
        {
            get { return this.killer; }
        }
        public Player Victim
        {
            get { return this.victim; }
        }
    }

    /// <summary>
    /// Base class for a player.
    /// </summary>
    public abstract class Player : ModelGameObject
    {
        private bool isMoving;

        //private BasicShaderEffect effect;

        //public BasicShaderEffect Effect
        //{
        //    get { return effect; }
        //    set { effect = value; }
        //}
        protected Weapons.WeaponBase currentWeapon;
        protected float mass = 50f;
        protected Body body;
        private Vector3 eyePosition;
        private int hp = 100;

        private string name;
        private int kills;
        private int deaths;

        protected float MinHP = 0.0f;
        protected float MaxHP = 100.0f;
        protected float MinAmmo = 0.0f;
        protected float MaxAmmo = 100.0f;

        public event EventHandler OnSpawn;
        public event EventHandler<PlayerDieEventArgs> OnDeath;
        public event EventHandler<PlayerKillEventArgs> OnKill;

        /// <summary>
        /// Gets or sets the hp of this player.
        /// </summary>
        public int Hp
        {
            get { return this.hp; }
            set { this.hp = value; }
        }
        /// <summary>
        /// Gets or sets the ammo of this player.
        /// </summary>
        public int Ammo
        {
            get { return this.currentWeapon.Ammunition; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        
        public int Kills
        {
            get { return kills; }
            set { kills = value; }
        }
        
        public int Deaths
        {
            get { return deaths; }
            set { deaths = value; }
        }

        public int Score() {
            return this.Kills - this.Deaths;
        }

        /// <summary>
        /// Creates a new instance of the Player class.
        /// </summary>
        /// <param name="game">The game that this instance belongs to.</param>
        /// <param name="playerModel">The asset name for model used for this player.</param>
        public Player(ERFGame game, string playerModel)
            : base(game)
        {
            base.ObjectModel = game.Content.Load<Model>(string.Format("Models\\{0}", playerModel));
            base.scale = new Vector3(0.011f, 0.011f, 0.011f);
            this.CreateBody();
            this.SetEffect();
            this.SetWeapon();
        }

        public void Died(Player player)
        {
            this.OnDeath(this, new PlayerDieEventArgs(player, this));
        }

        public virtual void TakeDamage(int dmg, NetworkPlayer np) {
            this.Hp -= dmg;
        }

        public void Killed(Player player) {
            this.OnKill(this, new PlayerKillEventArgs(this, player));
        }

        /// <summary>
        /// Creates the weapon.
        /// </summary>
        protected void SetWeapon()
        {
            this.currentWeapon = new Weapons.AK47(game, this);
            this.currentWeapon.Position = this.currentWeapon.PositionOffset;
            this.Children.Add(this.currentWeapon);
        }

        /// <summary>
        /// Creates the physical body.
        /// </summary>
        protected virtual void CreateBody()
        {
            BoundingBox box = new BoundingBox(
                new Vector3(-0.5f, -1f, -0.5f),
                new Vector3(0.5f, 1f, 0.5f));

            this.body = new Body(Vector3.Zero, box, 0, 0, false, this.mass, this);
            this.body.BodyChanged += new EventHandler(body_BodyChanged);
            this.body.BodyCollided += new EventHandler<PhysicsEngine.BodyCollidedEventArgs>(body_BodyCollided);
        }

        protected virtual void body_BodyCollided(object sender, PhysicsEngine.BodyCollidedEventArgs e)
        {
            //if the object that the player collides with is a pickupable
            if (e.Other.Owner is Pickups.Pickable)
            {
                //collision with the body with the pickup object as owner
                Pickups.Pickable item = (Pickable)e.Other.Owner;

                item.PickedUp(this);
            }
        }

        /// <summary>
        /// Handles the players Bodys BodyChanged event, and repositions the visual representation of the player (in other words, the model), accordingly.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void body_BodyChanged(object sender, EventArgs e)
        {
            Vector3 feetPosition = this.body.position;
            feetPosition.Y -= this.body.boundingBox.Max.Y;
            this.position = feetPosition;

            this.eyePosition = this.body.position;
            this.eyePosition.Y -= this.body.boundingBox.Min.Y;
            this.eyePosition.Y -= 0.2f;
        }

        /// <summary>
        /// Gets or sets a value denoting if the player is moving.
        /// </summary>
        public bool IsMoving
        {
            get { return this.isMoving; }
            set { this.isMoving = value; }
        }

        /// <summary>
        /// Gets the physical body of the player.
        /// </summary>
        public Body PhysicsBody
        {
            get { return this.body; }
        }

        /// <summary>
        /// Gets or sets the position of the player. Overrides the underlying position property in ModelgameObject.
        /// </summary>
        public new Vector3 Position {
            get { return this.PhysicsBody.position; }
            set { this.PhysicsBody.position = value; }
        }

        /// <summary>
        /// Gets or sets the eye position of this player.
        /// </summary>
        public Vector3 EyePosition
        {
            get { return this.eyePosition; }
            protected set { this.eyePosition = value; }
        }

        /// <summary>
        /// Gets the weapon in use by this player.
        /// </summary>
        public Weapons.WeaponBase CurrentWeapon
        {
            get { return this.currentWeapon; }
        }

        /// <summary>
        /// Sets the effects used on the meshes of this players model.
        /// </summary>
        protected virtual void SetEffect()
        {
            for (int i = 0; i < this.ObjectModel.Meshes.Count; i++)
            {
                for (int j = 0; j < this.ObjectModel.Meshes[i].MeshParts.Count; j++)
                {
                    BasicShaderEffect effect = new BasicShaderEffect(base.CurrentGame);

                    if (this.ObjectModel.Meshes[i].MeshParts[j].Effect is BasicEffect)
                    {
                        BasicEffect oldEffect = (BasicEffect)this.ObjectModel.Meshes[i].MeshParts[j].Effect;

                        effect = new BasicShaderEffect(base.CurrentGame);
                        effect.DiffuseColor = Color.Blue.ToVector3();
                        effect.LightDirection = new Vector3(0, 1, 1);
                        effect.AmbientColor = Color.White.ToVector3();
                        effect.AmbientIntensity = 0.1f;
                        effect.DiffuseIntensity = 1f;
                        effect.SpecularColor = Color.White.ToVector3();
                        effect.SpecularIntensity = 1f;
                        effect.SpecularPower = 16f;
                        effect.Texture = base.CurrentGame.Content.Load<Texture2D>("Textures\\Vincent_new_UV");
                        effect.EnableTexture = true;
                        effect.EnableLight = true;
                        effect.FogColor = Color.White.ToVector4();
                        effect.FogStart = 10.1f;
                        effect.FogEnd = 10f;
                        effect.FogEnabled = false;
                    }
                    else
                    {
                        BasicShaderEffect oldEffect = (BasicShaderEffect)this.ObjectModel.Meshes[i].MeshParts[j].Effect;

                        effect = new BasicShaderEffect(base.CurrentGame);
                        effect.DiffuseColor = Color.Blue.ToVector3();
                        effect.LightDirection = new Vector3(0, 1, 1);
                        effect.AmbientColor = Color.White.ToVector3();
                        effect.AmbientIntensity = 0.1f;
                        effect.DiffuseIntensity = 1f;
                        effect.SpecularColor = Color.White.ToVector3();
                        effect.SpecularIntensity = 1f;
                        effect.SpecularPower = 16f;
                        effect.Texture = base.CurrentGame.Content.Load<Texture2D>("Textures\\Vincent_new_UV");
                        effect.EnableTexture = true;
                        effect.EnableLight = true;
                        effect.FogColor = Color.White.ToVector4();
                        effect.FogStart = 10.1f;
                        effect.FogEnd = 10f;
                        effect.FogEnabled = false;
                    }


                    this.meshEffects[i][j] = effect;
                }
            }
        }

        public override void Update(GameTime gameTime, ref Matrix world)
        {
            for (int i = 0; i < this.ObjectModel.Meshes.Count; i++)
                for (int j = 0; j < this.ObjectModel.Meshes[i].MeshParts.Count; j++)
                    this.meshEffects[i][j].EyePosition = base.CurrentGame.Camera.Position;
            base.Update(gameTime, ref world);
        }

        public override void Dispose()
        {
            /*
            //remove object from scene graph
            PhysicsEngine physicsEngine = (PhysicsEngine)game.Services.GetService(typeof(PhysicsEngine));
            physicsEngine.AddBody(this.PhysicsBody);
            game.CurrentSceneGraph.Items.Remove(this);
            */
            base.Dispose();
        }
    }
}
